import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import classnames from 'classnames'
import '@/animate.css'
import './index.scss'
interface Duration {
  enter?: number,
  leave?: number
}

interface Props {
  /**
   * @description: 动画名称
   * @value: fade | fadeDown | fadeDownBig | fadeUp | fadeUpBig | fadeLeft | fadeLeftBig | fadeRight | fadeRightBig | bounce | bounceDown | bounceUp | bounceLeft | bounceRight | flip | flipX | flipY | lightSpeed | rotate | rotateUpLeft | rotateUpRight | rotateDownLeft | rotateDownRight | roll | hangOut | rubberBand | shake | jump | heartbeat | flash | swing | tada | wobble
   */
  name?: string,
  // 是否显示
  show?: boolean,
  // 是否渲染
  render?: boolean,
  // 动画过渡时间
  duration?: number | Duration,
  // 动画延迟时间
  delay?: number,
  // 动画播放次数
  iterationCount?: number | string,
  // 动画开始的回调
  onAnimationStart?: Function,
  // 动画结束的回调
  onAnimationEnd?: Function
}

// 预设动画类型名称
const nameOfAnimations = {
  // 淡入淡出
  fade: { enter: 'fadeIn', leave: 'fadeOut' },
  // 向下淡入淡出
  fadeDown: { enter: 'fadeInDown', leave: 'fadeOutUp' },
  // 向下淡入淡出-大幅度
  fadeDownBig: { enter: 'fadeInDownBig', leave: 'fadeOutUpBig' },
  // 向上淡入淡出
  fadeUp: { enter: 'fadeInUp', leave: 'fadeOutDown' },
  // 向上淡入淡出-大幅度
  fadeUpBig: { enter: 'fadeInUpBig', leave: 'fadeOutDownBig' },
  // 向左淡入淡出
  fadeLeft: { enter: 'fadeInLeft', leave: 'fadeOutLeft' },
  // 向左淡入淡出-大幅度
  fadeLeftBig: { enter: 'fadeInLeftBig', leave: 'fadeOutLeftBig' },
  // 向右淡入淡出
  fadeRight: { enter: 'fadeInRight', leave: 'fadeOutRight' },
  // 向右淡入淡出-大幅度
  fadeRightBig: { enter: 'fadeInRightBig', leave: 'fadeOutRightBig' },
  // 中间弹入弹出
  bounce: { enter: 'bounceIn', leave: 'bounceOut' },
  // 向下弹入弹出
  bounceDown: { enter: 'bounceInDown', leave: 'bounceOutUp' },
  // 向上弹入弹出
  bounceUp: { enter: 'bounceInUp', leave: 'bounceOutDown' },
  // 向左弹入弹出
  bounceLeft: { enter: 'bounceInLeft', leave: 'bounceOutLeft' },
  // 向右弹入弹出
  bounceRight: { enter: 'bounceInRight', leave: 'bounceOutRight' },
  // 3d旋转
  flip: { enter: 'flip', leave: 'fadeOut' },
  // x轴旋转晃动
  flipX: { enter: 'flipInX', leave: 'flipOutX' },
  // y轴旋转晃动
  flipY: { enter: 'flipInY', leave: 'flipOutY' },
  // 从右侧向左移入
  lightSpeed: { enter: 'lightSpeedIn', leave: 'lightSpeedOut' },
  // 旋转进入离开
  rotate: { enter: 'rotateIn', leave: 'rotateOut' },
  // 从左上角旋转进入
  rotateUpLeft: { enter: 'rotateInDownLeft', leave: 'rotateOutUpLeft' },
  // 从右上角旋转进入
  rotateUpRight: { enter: 'rotateInDownRight', leave: 'rotateOutUpRight' },
  // 从左下角旋转进入
  rotateDownLeft: { enter: 'rotateInUpLeft', leave: 'rotateOutDownLeft' },
  // 从右下角旋转进入
  rotateDownRight: { enter: 'rotateInUpRight', leave: 'rotateOutDownRight' },
  // 飘入飘出
  roll: { enter: 'rollIn', leave: 'rollOut' },
  // 悬挂离开
  hang: { enter: 'fadeIn', leave: 'hinge' },
  // 橡皮筋似的弹动
  rubberBand: { enter: 'rubberBand' },
  // 震动
  shake: { enter: 'shake' },
  // 向上跳动
  jump: { enter: 'bounce' },
  // 心跳
  heartbeat: { enter: 'pulse' },
  // 闪烁
  flash: { enter: 'flash' },
  // 摆动
  swing: { enter: 'swing' },
  // 两侧抖动
  tada: { enter: 'tada' },
  // 左右晃动
  wobble: { enter: 'wobble' }
}

interface State {
  animatorClass: string[],
  animatorStyle: string,
  animatorRender: boolean,
  animationIsStart: boolean
}

const DefaultDuration = 750

/**
 * 页面元素动画组件
 */
export default class Index extends Component<Props> {
  // 外部样式
  static externalClasses = ['enter-class', 'leave-class', 'custom-class']

  // props默认值
  static defaultProps = {
    name: '',
    show: true,
    render: true,
    duration: DefaultDuration,
    delay: 0,
    iterationCount: 1
  }

  state: State = {
    // 动画元素节点的class
    animatorClass: [],
    // 动画元素节点的style样式
    animatorStyle: '',
    // 是否渲染元素节点
    animatorRender: true,
    // 动画是否开始播放
    animationIsStart: false
  }

  componentDidMount() {
    this.updateStatus()
  }

  componentWillReceiveProps(nextProps: Props) {
    if (JSON.stringify(this.props) === JSON.stringify(nextProps)) return
    this.updateStatus(nextProps)
  }

  // 动画开始
  animationStartHandler() {
    console.log('动画开始的回调')
    // 父组件动画开始的回调
    const { onAnimationStart } = this.props
    onAnimationStart && onAnimationStart()

    this.setState({ animationIsStart: true })
  }

  // 动画结束
  animationEndHandler() {
    console.log('动画结束的回调')
    const { animationIsStart } = this.state
    const { show, render } = this.props
    let animatorClass: string[] = []

    // 不渲染节点
    if (!render) {
      this.setState({ animatorRender: false })
    }

    // 渲染但不现实节点
    if (render && !show) {
      // 如果是离开动画，则动画结束后添加hidden隐藏元素
      animatorClass.push('hidden')
    }
    // 重置动画状态
    this.setState({ animatorClass, animatorStyle: '', animationIsStart: false })

    // 动画经历过开始播放阶段，才执行结束回调
    // 否则应该是没有配置动画的情况，不需要执行动画结束回调
    if (animationIsStart) {
      // 父组件动画结束的回调
      const { onAnimationEnd } = this.props
      onAnimationEnd && onAnimationEnd()
    }
  }

  /**
   * 更新动画状态
   * @param {Props} nextProps - 父组件更新状态时传入的props
   * @returns {*}
   */
  updateStatus(nextProps?: Props): void {
    console.log('更新动画')
    // 有状态更新立即修改动画开始标记的状态
    this.setState({ animationIsStart: false })
    let { render, show, name, duration, delay, iterationCount } = nextProps || this.props
    let enterClass: string = 'enter-class'
    let leaveClass: string = 'leave-class'
    let animatorStyle: string = ''
    // 动画参数错误标记
    let wrong = false
    // 动画类型: 如果name有值, 则忽略enter-class, leave-class
    if (name) {
      const animation = nameOfAnimations[name]
      if (animation) {
        enterClass = animation.enter || ''
        leaveClass = animation.leave || ''
      } else {
        wrong = true
      }
    }

    // 显示和隐藏时没有对应的动画样式，则不作动画细节处理
    if (!wrong) {
      // 动画延迟
      if (delay) {
        animatorStyle += `animation-delay: ${delay}ms;`
      }

      // 动画过渡时间
      if (typeof duration === 'number') {
        animatorStyle += `animation-duration: ${duration}ms;`
      }
      if (typeof duration === 'object') {
        animatorStyle += `animation-duration: ${show ? (duration.enter || DefaultDuration) : (duration.leave || DefaultDuration)}ms;`
      }

      // 动画播放次数
      if (typeof iterationCount === 'number' || (typeof iterationCount === 'string' && iterationCount === 'infinite')) {
        animatorStyle += `animation-iteration-count: ${iterationCount};`
      }
    }

    // 添加动画状态
    this.setState({
      animatorClass: [render && show ? enterClass : leaveClass],
      animatorStyle,
      animatorRender: true
    })

    // 针对无动画场景，在没有name和enter-class,leave-class时，不会有动画，所以需要立即调用animationEnd回调，显示隐藏元素
    // 从添加动画样式到触发动画开始事件大约要延迟30~80毫秒，所以在80ms后才能检测到是否有动画
    // 有出错的几率
    // setTimeout(() => {
    //   if (!this.state.animationIsStart) {
    //     this.animationEndHandler(true)
    //   }
    // }, 300)
  }

  render() {
    const { animatorClass, animatorStyle, animatorRender } = this.state
    if (!animatorRender) return
    return (
      <View
        ref="animator"
        className={classnames('custom-class', 'animated', animatorClass)}
        style={animatorStyle}
        onAnimationStart={this.animationStartHandler}
        onAnimationEnd={this.animationEndHandler}
      >
        {this.props.children}
      </View>
    )
  }
}
